/*
Copyright 2011 Savior
http://code.google.com/p/savior

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package ca.pgon.saviorgui.GUI;

import ca.pgon.saviorgui.EngineThread;
import ca.pgon.saviorgui.profile.Profile;
import ca.pgon.saviorgui.profile.ProfilesManager;
import ca.pgon.saviorlib.CheckMods.CheckMod;
import ca.pgon.saviorlib.CheckMods.DateCM;
import ca.pgon.saviorlib.CheckMods.MD5CM;
import ca.pgon.saviorlib.CheckMods.SizeCM;
import ca.pgon.saviorlib.Engines.AppendOnlyEngine;
import ca.pgon.saviorlib.Engines.Engine;
import ca.pgon.saviorlib.Engines.SyncEngine;
import ca.pgon.saviorlib.Events.AddEvent;
import ca.pgon.saviorlib.Events.AddProgressType;
import ca.pgon.saviorlib.Events.BackupEvent;
import ca.pgon.saviorlib.Events.BackupType;
import ca.pgon.saviorlib.Events.ChangeDirectoryEvent;
import ca.pgon.saviorlib.Events.CreateDirectoryEvent;
import ca.pgon.saviorlib.Events.DeleteDirectoryEvent;
import ca.pgon.saviorlib.Events.DeleteEvent;
import ca.pgon.saviorlib.Events.ProgressEvent;
import ca.pgon.saviorlib.Exceptions.FileSystemException;
import ca.pgon.saviorlib.FileSystems.FTPFS;
import ca.pgon.saviorlib.FileSystems.FileEntry;
import ca.pgon.saviorlib.FileSystems.FileSystem;
import ca.pgon.saviorlib.FileSystems.FileSystemTools;
import ca.pgon.saviorlib.FileSystems.LocalFS;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JOptionPane;

public class Principal extends javax.swing.JFrame implements AddEvent, BackupEvent, ChangeDirectoryEvent, CreateDirectoryEvent, DeleteDirectoryEvent, DeleteEvent, ProgressEvent {
    static private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private Engine engine;
    private Profile currentProfile;
    
    /** Creates new form Principal */
    public Principal() {
        initComponents();
        
        setEmptyProfile();
        updateProfilesList();
        updateViewFromProfile();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        sourceBasePath = new javax.swing.JTextField();
        jSeparator1 = new javax.swing.JSeparator();
        jLabel3 = new javax.swing.JLabel();
        jSeparator2 = new javax.swing.JSeparator();
        destinationBasePath = new javax.swing.JTextField();
        jLabel4 = new javax.swing.JLabel();
        jLabel5 = new javax.swing.JLabel();
        jSeparator3 = new javax.swing.JSeparator();
        checkDate = new javax.swing.JCheckBox();
        CheckSize = new javax.swing.JCheckBox();
        CheckMD5 = new javax.swing.JCheckBox();
        startButton = new javax.swing.JButton();
        progress = new javax.swing.JProgressBar();
        stopButton = new javax.swing.JButton();
        pauseToggle = new javax.swing.JToggleButton();
        sourceBrowse = new javax.swing.JButton();
        destinationBrowse = new javax.swing.JButton();
        jLabel6 = new javax.swing.JLabel();
        profilesList = new javax.swing.JComboBox();
        saveProfile = new javax.swing.JButton();
        saveAsProfile = new javax.swing.JButton();
        deleteProfile = new javax.swing.JButton();
        sourceFileSystem = new javax.swing.JComboBox();
        sourceSetConnection = new javax.swing.JButton();
        destinationSetConnection = new javax.swing.JButton();
        destinationFileSystem = new javax.swing.JComboBox();
        jLabel7 = new javax.swing.JLabel();
        backupEngine = new javax.swing.JComboBox();
        engineHelpButton = new javax.swing.JButton();
        jSeparator4 = new javax.swing.JSeparator();
        jLabel8 = new javax.swing.JLabel();
        jScrollPane2 = new javax.swing.JScrollPane();
        ignoreList = new javax.swing.JTextArea();
        ignoreSource = new javax.swing.JButton();
        ignoreDestination = new javax.swing.JButton();
        jTabbedPane1 = new javax.swing.JTabbedPane();
        jScrollPane1 = new javax.swing.JScrollPane();
        fullLog = new javax.swing.JTextArea();
        jScrollPane3 = new javax.swing.JScrollPane();
        scanLog = new javax.swing.JTextArea();
        jScrollPane4 = new javax.swing.JScrollPane();
        addLog = new javax.swing.JTextArea();
        jScrollPane5 = new javax.swing.JScrollPane();
        delLog = new javax.swing.JTextArea();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Savior");
        setName("Principal"); // NOI18N

        jLabel1.setFont(new java.awt.Font("Tahoma", 1, 18));
        jLabel1.setText("Source");

        jLabel2.setText("Path");

        jLabel3.setFont(new java.awt.Font("Tahoma", 1, 18));
        jLabel3.setText("Destination");

        jLabel4.setText("Path");

        jLabel5.setFont(new java.awt.Font("Tahoma", 1, 18));
        jLabel5.setText("Modification checks");

        checkDate.setText("Date");

        CheckSize.setText("Size");

        CheckMD5.setText("MD5");

        startButton.setText("Start");
        startButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                startButtonActionPerformed(evt);
            }
        });

        stopButton.setText("Stop");
        stopButton.setEnabled(false);
        stopButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                stopButtonActionPerformed(evt);
            }
        });

        pauseToggle.setText("Pause");
        pauseToggle.setEnabled(false);
        pauseToggle.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                pauseToggleActionPerformed(evt);
            }
        });

        sourceBrowse.setText("Browse");
        sourceBrowse.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                sourceBrowseActionPerformed(evt);
            }
        });

        destinationBrowse.setText("Browse");
        destinationBrowse.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                destinationBrowseActionPerformed(evt);
            }
        });

        jLabel6.setFont(new java.awt.Font("Tahoma", 1, 18));
        jLabel6.setText("Profiles");

        profilesList.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                profilesListActionPerformed(evt);
            }
        });

        saveProfile.setText("Save");
        saveProfile.setEnabled(false);
        saveProfile.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveProfileActionPerformed(evt);
            }
        });

        saveAsProfile.setText("Save as...");
        saveAsProfile.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveAsProfileActionPerformed(evt);
            }
        });

        deleteProfile.setText("Delete");
        deleteProfile.setEnabled(false);
        deleteProfile.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                deleteProfileActionPerformed(evt);
            }
        });

        sourceFileSystem.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Local", "FTP" }));
        sourceFileSystem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                sourceFileSystemActionPerformed(evt);
            }
        });

        sourceSetConnection.setText("Set connection infos...");
        sourceSetConnection.setEnabled(false);
        sourceSetConnection.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                sourceSetConnectionActionPerformed(evt);
            }
        });

        destinationSetConnection.setText("Set connection infos...");
        destinationSetConnection.setEnabled(false);
        destinationSetConnection.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                destinationSetConnectionActionPerformed(evt);
            }
        });

        destinationFileSystem.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Local", "FTP" }));
        destinationFileSystem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                destinationFileSystemActionPerformed(evt);
            }
        });

        jLabel7.setFont(new java.awt.Font("Tahoma", 1, 18));
        jLabel7.setText("Backup Engine");

        backupEngine.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Sync", "AppendOnly" }));

        engineHelpButton.setText("?");
        engineHelpButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                engineHelpButtonActionPerformed(evt);
            }
        });

        jLabel8.setFont(new java.awt.Font("Tahoma", 1, 18));
        jLabel8.setText("Directory & file ignored");

        ignoreList.setColumns(20);
        ignoreList.setRows(5);
        jScrollPane2.setViewportView(ignoreList);

        ignoreSource.setText("Add from source");
        ignoreSource.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                ignoreSourceActionPerformed(evt);
            }
        });

        ignoreDestination.setText("Add from destination");
        ignoreDestination.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                ignoreDestinationActionPerformed(evt);
            }
        });

        fullLog.setColumns(20);
        fullLog.setRows(5);
        jScrollPane1.setViewportView(fullLog);

        jTabbedPane1.addTab("Full", jScrollPane1);

        scanLog.setColumns(20);
        scanLog.setRows(5);
        jScrollPane3.setViewportView(scanLog);

        jTabbedPane1.addTab("Scanning", jScrollPane3);

        addLog.setColumns(20);
        addLog.setRows(5);
        jScrollPane4.setViewportView(addLog);

        jTabbedPane1.addTab("Adding", jScrollPane4);

        delLog.setColumns(20);
        delLog.setRows(5);
        jScrollPane5.setViewportView(delLog);

        jTabbedPane1.addTab("Deleting", jScrollPane5);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addContainerGap()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabel6)
                            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                                .addComponent(profilesList, 0, 704, Short.MAX_VALUE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(deleteProfile)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(saveProfile)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(saveAsProfile))
                            .addComponent(jLabel1)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(sourceFileSystem, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addGap(18, 18, 18)
                                .addComponent(sourceSetConnection))
                            .addComponent(jSeparator1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 925, Short.MAX_VALUE)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(jLabel2)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(sourceBasePath, javax.swing.GroupLayout.DEFAULT_SIZE, 826, Short.MAX_VALUE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(sourceBrowse))
                            .addComponent(jLabel3)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(destinationFileSystem, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addGap(18, 18, 18)
                                .addComponent(destinationSetConnection))
                            .addComponent(jSeparator2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 925, Short.MAX_VALUE)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(jLabel4)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(destinationBasePath, javax.swing.GroupLayout.DEFAULT_SIZE, 826, Short.MAX_VALUE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(destinationBrowse))
                            .addGroup(layout.createSequentialGroup()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                                    .addGroup(layout.createSequentialGroup()
                                        .addComponent(backupEngine, javax.swing.GroupLayout.PREFERRED_SIZE, 119, javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(engineHelpButton)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 217, Short.MAX_VALUE)
                                        .addComponent(ignoreSource))
                                    .addComponent(ignoreDestination))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 429, javax.swing.GroupLayout.PREFERRED_SIZE))
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(jLabel7)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 581, Short.MAX_VALUE)
                                .addComponent(jLabel8))))
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addGap(14, 14, 14)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(progress, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 921, Short.MAX_VALUE)
                            .addComponent(jLabel5)
                            .addComponent(jSeparator4, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 921, Short.MAX_VALUE)
                            .addGroup(layout.createSequentialGroup()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addGroup(layout.createSequentialGroup()
                                        .addComponent(checkDate)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(CheckSize)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(CheckMD5))
                                    .addComponent(jSeparator3, javax.swing.GroupLayout.DEFAULT_SIZE, 644, Short.MAX_VALUE)
                                    .addGroup(layout.createSequentialGroup()
                                        .addComponent(startButton)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(stopButton)
                                        .addGap(18, 18, 18)
                                        .addComponent(pauseToggle)))
                                .addGap(277, 277, 277))))
                    .addGroup(layout.createSequentialGroup()
                        .addContainerGap()
                        .addComponent(jTabbedPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 925, Short.MAX_VALUE)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel6)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(profilesList, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(saveAsProfile)
                    .addComponent(saveProfile)
                    .addComponent(deleteProfile))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(sourceFileSystem, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(sourceSetConnection))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel2)
                    .addComponent(sourceBasePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(sourceBrowse))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel3)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(destinationFileSystem, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(destinationSetConnection))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel4)
                    .addComponent(destinationBasePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(destinationBrowse))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel7)
                    .addComponent(jLabel8))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(backupEngine, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(engineHelpButton)
                            .addComponent(ignoreSource))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(ignoreDestination))
                    .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 106, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(jSeparator4, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(5, 5, 5)
                .addComponent(jLabel5)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(checkDate)
                    .addComponent(CheckSize)
                    .addComponent(CheckMD5))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jSeparator3, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(startButton)
                    .addComponent(stopButton)
                    .addComponent(pauseToggle))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jTabbedPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 269, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(progress, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void startButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_startButtonActionPerformed
        fullLog.setText("");
        scanLog.setText("");
        addLog.setText("");
        delLog.setText("");
        
        FileSystem sourceFS = createSource();
        FileSystem destinationFS = createDestination();
        
        List<CheckMod> checkMods = new ArrayList<CheckMod>();
        if (checkDate.isSelected()) {
            DateCM dcm = new DateCM();
            
            if ("FTP".equals(currentProfile.sourceFileSystemType) || "FTP".equals(currentProfile.destinationFileSystemType)) {
                dcm.setType(DateCM.Type.DESTINATION_OLDER);
            }
            
            checkMods.add(dcm);
        }
        if (CheckSize.isSelected()) {
            checkMods.add(new SizeCM());
        }
        if (CheckMD5.isSelected()) {
            checkMods.add(new MD5CM());
        }
        
        if ("AppendOnly".equals(currentProfile.engineType)) {
            engine = new AppendOnlyEngine();
        } else {
            engine = new SyncEngine();
        }
        
        engine.setSourceFileSystem(sourceFS);
        engine.setDestinationFileSystem(destinationFS);
        engine.setCheckMods(checkMods);
        engine.setIgnoreFiles(currentProfile.ignoreList);
        
        engine.setAddEvent(this);
        engine.setBackupEvent(this);
        engine.setChangeDirectoryEvent(this);
        engine.setCreateDirectoryEvent(this);
        engine.setDeleteDirectoryEvent(this);
        engine.setDeleteEvent(this);
        engine.setProgressEvent(this);
        
        EngineThread et = new EngineThread(engine);
        et.start();
    }//GEN-LAST:event_startButtonActionPerformed

    private void stopButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_stopButtonActionPerformed
        engine.stop();
    }//GEN-LAST:event_stopButtonActionPerformed

    private void pauseToggleActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pauseToggleActionPerformed
        if (pauseToggle.isSelected()) {
            engine.pause();
        } else {
            engine.resume();
        }
    }//GEN-LAST:event_pauseToggleActionPerformed

    private void sourceBrowseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sourceBrowseActionPerformed
        FileSystem sourceFS = createSource();

        try {
            sourceFS.checkIfValid();
        } catch (FileSystemException ex) {
            sourceFS.setBasePath("");
        }
        FileEntry fe = new FileEntry();
        fe.fileSystem = sourceFS;
        fe.path = "";
        fe.name = "";
        fe.isDirectory = true;
        boolean rootHasSlash = !("Local".equals(currentProfile.sourceFileSystemType) && System.getProperty("os.name").startsWith("Windows"));
        BasePathBrowseDialog bd = new BasePathBrowseDialog(this, true, sourceBasePath, fe, rootHasSlash);
        bd.setVisible(true);
    }//GEN-LAST:event_sourceBrowseActionPerformed

    private void destinationBrowseActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_destinationBrowseActionPerformed
        FileSystem destinationFS = createDestination();
        
        try {
            destinationFS.checkIfValid();
        } catch (FileSystemException ex) {
            destinationFS.setBasePath("");
        }
        FileEntry fe = new FileEntry();
        fe.fileSystem = destinationFS;
        fe.path = "";
        fe.name = "";
        fe.isDirectory = true;
        boolean rootHasSlash = !("Local".equals(currentProfile.destinationFileSystemType) && System.getProperty("os.name").startsWith("Windows"));
        BasePathBrowseDialog bd = new BasePathBrowseDialog(this, true, destinationBasePath, fe, rootHasSlash);
        bd.setVisible(true);
    }//GEN-LAST:event_destinationBrowseActionPerformed

    private void saveProfileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveProfileActionPerformed
        updateProfileFromView();
        
        if ("".equals(profilesList.getSelectedItem())) {
            saveAsProfileActionPerformed(null);
        } else {
            ProfilesManager.saveProfile((String) profilesList.getSelectedItem(), currentProfile);
        }
    }//GEN-LAST:event_saveProfileActionPerformed

    private void saveAsProfileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveAsProfileActionPerformed
        updateProfileFromView();
        
        String name = JOptionPane.showInputDialog("What is the filename?");
        if (name == null) return;
        
        ProfilesManager.saveProfile(name, currentProfile);
        updateProfilesList();
        profilesList.setSelectedItem(name);
    }//GEN-LAST:event_saveAsProfileActionPerformed

    private void profilesListActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_profilesListActionPerformed
        if ("".equals(profilesList.getSelectedItem())) {
            deleteProfile.setEnabled(false);
            saveProfile.setEnabled(false);
            setEmptyProfile();
        } else {
            deleteProfile.setEnabled(true);
            saveProfile.setEnabled(true);
            currentProfile = ProfilesManager.loadProfile((String) profilesList.getSelectedItem());
        }
        
        updateViewFromProfile();
    }//GEN-LAST:event_profilesListActionPerformed

    private void deleteProfileActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteProfileActionPerformed
        if (!"".equals(profilesList.getSelectedItem())) {
            ProfilesManager.deleteProfile((String) profilesList.getSelectedItem());
        }
        
        updateProfilesList();
    }//GEN-LAST:event_deleteProfileActionPerformed

    private void sourceFileSystemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sourceFileSystemActionPerformed
        sourceSetConnection.setEnabled("FTP".equals(sourceFileSystem.getSelectedItem()));
        if (!currentProfile.sourceFileSystemType.equals(sourceFileSystem.getSelectedItem())) {
            currentProfile.sourceParams = new HashMap<String, String>();
        }
    }//GEN-LAST:event_sourceFileSystemActionPerformed

    private void sourceSetConnectionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sourceSetConnectionActionPerformed
        showSetConnectionInfo(currentProfile.sourceParams);
    }//GEN-LAST:event_sourceSetConnectionActionPerformed

    private void destinationSetConnectionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_destinationSetConnectionActionPerformed
        showSetConnectionInfo(currentProfile.destinationParams);
    }//GEN-LAST:event_destinationSetConnectionActionPerformed

    private void destinationFileSystemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_destinationFileSystemActionPerformed
        destinationSetConnection.setEnabled("FTP".equals(destinationFileSystem.getSelectedItem()));
        if (!currentProfile.destinationFileSystemType.equals(destinationFileSystem.getSelectedItem())) {
            currentProfile.destinationParams = new HashMap<String, String>();
        }

    }//GEN-LAST:event_destinationFileSystemActionPerformed

    private void engineHelpButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_engineHelpButtonActionPerformed
        JOptionPane.showMessageDialog(this, "Sync: At the end, the destination will have the same files and directories than the source\nAppendOnly: Files and directory not present in the source will not be deleted in the destination");
    }//GEN-LAST:event_engineHelpButtonActionPerformed

    private void ignoreSourceActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ignoreSourceActionPerformed
        FileSystem fs = createSource();
        FileEntry fileEntry = FileSystemTools.createFileEntry(fs, true, "", "", 0, 0);
        IgnoreBrowseDialog ignore = new IgnoreBrowseDialog(this, true, fileEntry);
        ignore.setVisible(true);
        
        if (ignore.okpressed) {
            for (FileEntry fe : ignore.entries) {
                ignoreList.append(FileSystemTools.getRelativePath(fe));
                ignoreList.append("\n");
            }
            
            updateProfileFromView();
        }
    }//GEN-LAST:event_ignoreSourceActionPerformed

    private void ignoreDestinationActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ignoreDestinationActionPerformed
        FileSystem fs = createDestination();
        FileEntry fileEntry = FileSystemTools.createFileEntry(fs, true, "", "", 0, 0);
        IgnoreBrowseDialog ignore = new IgnoreBrowseDialog(this, true, fileEntry);
        ignore.setVisible(true);
        
        if (ignore.okpressed) {
            for (FileEntry fe : ignore.entries) {
                ignoreList.append(FileSystemTools.getRelativePath(fe));
                ignoreList.append("\n");
            }
            
            updateProfileFromView();
        }
    }//GEN-LAST:event_ignoreDestinationActionPerformed

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Principal().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JCheckBox CheckMD5;
    private javax.swing.JCheckBox CheckSize;
    private javax.swing.JTextArea addLog;
    private javax.swing.JComboBox backupEngine;
    private javax.swing.JCheckBox checkDate;
    private javax.swing.JTextArea delLog;
    private javax.swing.JButton deleteProfile;
    private javax.swing.JTextField destinationBasePath;
    private javax.swing.JButton destinationBrowse;
    private javax.swing.JComboBox destinationFileSystem;
    private javax.swing.JButton destinationSetConnection;
    private javax.swing.JButton engineHelpButton;
    private javax.swing.JTextArea fullLog;
    private javax.swing.JButton ignoreDestination;
    private javax.swing.JTextArea ignoreList;
    private javax.swing.JButton ignoreSource;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JLabel jLabel8;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JScrollPane jScrollPane4;
    private javax.swing.JScrollPane jScrollPane5;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JSeparator jSeparator2;
    private javax.swing.JSeparator jSeparator3;
    private javax.swing.JSeparator jSeparator4;
    private javax.swing.JTabbedPane jTabbedPane1;
    private javax.swing.JToggleButton pauseToggle;
    private javax.swing.JComboBox profilesList;
    private javax.swing.JProgressBar progress;
    private javax.swing.JButton saveAsProfile;
    private javax.swing.JButton saveProfile;
    private javax.swing.JTextArea scanLog;
    private javax.swing.JTextField sourceBasePath;
    private javax.swing.JButton sourceBrowse;
    private javax.swing.JComboBox sourceFileSystem;
    private javax.swing.JButton sourceSetConnection;
    private javax.swing.JButton startButton;
    private javax.swing.JButton stopButton;
    // End of variables declaration//GEN-END:variables

    public void appendLog(String text, boolean scan, boolean add, boolean del) {
        StringBuilder sb = new StringBuilder();
        sb.append(sdf.format(new Date()));
        sb.append(" ");
        sb.append(text);
        
        fullLog.append(sb.toString());
        fullLog.setCaretPosition(fullLog.getText().length() - 1);
        
        if (scan) {
            scanLog.append(sb.toString());
            scanLog.setCaretPosition(scanLog.getText().length() - 1);
        }
        
        if (add) {
            addLog.append(sb.toString());
            addLog.setCaretPosition(addLog.getText().length() - 1);
        }
        
        if (del) {
            delLog.append(sb.toString());
            delLog.setCaretPosition(delLog.getText().length() - 1);
        }
    }
    
    @Override
    public void addEventHandler(FileEntry destination, AddProgressType progessType) {
        appendLog("Adding " + FileSystemTools.getRelativePath(destination) + " " + progessType + "\n", false, true, false);
        if (progessType == AddProgressType.COMPLETED) {
            progress.setValue((int) destination.size);
        }
    }
    
    @Override
    public void backupEventHandler(BackupType backupType) {
        appendLog("Backup: " + backupType + "\n", false, false, false);
        
        switch (backupType) {
            case STARTING:
                startButton.setEnabled(false);
                stopButton.setEnabled(true);
                pauseToggle.setEnabled(true);
                pauseToggle.setSelected(false);
                break;
            case PAUSING:
            case RESUMING:
                break;
            case ABORTING:
            case COMPLETED:
                startButton.setEnabled(true);
                stopButton.setEnabled(false);
                pauseToggle.setEnabled(false);
                break;
        }
    }
    
    @Override
    public void changeDirectoryEventHandler(FileEntry source) {
        appendLog("Scanning: " + FileSystemTools.getRelativePath(source) + "\n", true, false, false);
    }

    @Override
    public void createDirectoryEventHandler(FileEntry destination) {
        appendLog("Creating directory " + FileSystemTools.getRelativePath(destination) + "\n", false, true, false);
    }

    @Override
    public void deleteDirectoryEventHandler(FileEntry destination) {
        appendLog("Deleting directory " + FileSystemTools.getRelativePath(destination) + "\n", false, false, true);
    }

    @Override
    public void deleteEventHandler(FileEntry destination) {
        appendLog("Deleting file " + FileSystemTools.getRelativePath(destination) + "\n", false, false, true);
    }

    @Override
    public void progressEventHandler(FileEntry destination, long currentPos) {
        progress.setMaximum((int) destination.size);
        progress.setValue((int) currentPos);
    }
    
    private void updateViewFromProfile() {
        sourceFileSystem.setSelectedItem(currentProfile.sourceFileSystemType);
        sourceSetConnection.setEnabled("FTP".equals(currentProfile.sourceFileSystemType));
        sourceBasePath.setText(currentProfile.sourceBasePath);
        
        destinationFileSystem.setSelectedItem(currentProfile.destinationFileSystemType);
        destinationSetConnection.setEnabled("FTP".equals(currentProfile.destinationFileSystemType));
        destinationBasePath.setText(currentProfile.destinationBasePath);
        
        backupEngine.setSelectedItem(currentProfile.engineType);
        
        checkDate.setSelected(currentProfile.modDate);
        CheckSize.setSelected(currentProfile.modSize);
        CheckMD5.setSelected(currentProfile.modMD5);
        
        ignoreList.setText("");
        if (currentProfile.ignoreList != null) {
            for (String s : currentProfile.ignoreList) {
                ignoreList.append(s);
                ignoreList.append("\n");
            }
        }
    }
    
    private void updateProfileFromView() {
        currentProfile.sourceFileSystemType = (String) sourceFileSystem.getSelectedItem();
        currentProfile.sourceBasePath = sourceBasePath.getText();
        
        currentProfile.destinationFileSystemType = (String) destinationFileSystem.getSelectedItem();
        currentProfile.destinationBasePath = destinationBasePath.getText();
        
        currentProfile.engineType = (String) backupEngine.getSelectedItem();
        
        currentProfile.modDate = checkDate.isSelected();
        currentProfile.modSize = CheckSize.isSelected();
        currentProfile.modMD5 = CheckMD5.isSelected();
        
        currentProfile.ignoreList = new ArrayList<String>();
        for (String s: ignoreList.getText().split("\n")) {
            if (!s.isEmpty()) {
                currentProfile.ignoreList.add(s);
            }
        }
    }

    private void updateProfilesList() {
        List<String> list = new ArrayList<String>();
        list.add("");
        list.addAll(Arrays.asList(ProfilesManager.listProfiles()));
        profilesList.setModel(new DefaultComboBoxModel(list.toArray()));
    }

    private void setEmptyProfile() {
        currentProfile = new Profile();
        currentProfile.sourceFileSystemType = "Local";
        currentProfile.destinationFileSystemType = "Local";
        currentProfile.engineType = "Sync";
        currentProfile.modDate = true;
        currentProfile.modSize = true;
        currentProfile.sourceParams = new HashMap<String, String>();
        currentProfile.destinationParams = new HashMap<String, String>();
        currentProfile.ignoreList = new ArrayList<String>();
    }
    
    private void showSetConnectionInfo(Map<String, String> params) {
        FTPConnectionInfo info = new FTPConnectionInfo(this, true);
        info.host.setText(params.get("host"));
        info.port.setText(params.get("port"));
        info.username.setText(params.get("username"));
        info.password.setText(params.get("password"));
        
        info.setVisible(true);
        
        if (info.okpressed) {
            params.put("host", info.host.getText());
            params.put("port", info.port.getText());
            params.put("username", info.username.getText());
            params.put("password", new String(info.password.getPassword()));
        }
    }
    
    private FileSystem createSource() {
        updateProfileFromView();
        
        FileSystem sourceFS;
        if ("FTP".equals(sourceFileSystem.getSelectedItem())) {
            sourceFS = createFTPFS(
                    currentProfile.sourceBasePath, 
                    currentProfile.sourceParams.get("host"), 
                    Integer.valueOf(currentProfile.sourceParams.get("port")),
                    currentProfile.sourceParams.get("username"), 
                    currentProfile.sourceParams.get("password"));
        } else {
            sourceFS = createLocalFS(sourceBasePath.getText());
        }
        
        return sourceFS;
    }
    
    private FileSystem createDestination() {
        updateProfileFromView();
        
        FileSystem destinationFS;
        if ("FTP".equals(destinationFileSystem.getSelectedItem())) {
            destinationFS = createFTPFS(
                    currentProfile.destinationBasePath, 
                    currentProfile.destinationParams.get("host"), 
                    Integer.valueOf(currentProfile.destinationParams.get("port")),
                    currentProfile.destinationParams.get("username"), 
                    currentProfile.destinationParams.get("password"));
        } else {
            destinationFS = createLocalFS(destinationBasePath.getText());
        }
        
        return destinationFS;
    }
    
    private FileSystem createLocalFS(String basepath) {
        LocalFS result = new LocalFS();
        result.setBasePath(basepath);
        return result;
    }
    
    private FileSystem createFTPFS(String basepath, String hostname, int port, String username, String pass) {
        FTPFS result = new FTPFS();
        result.setBasePath(basepath);
        result.setHostname(hostname);
        result.setPort(port);
        result.setUser(username);
        result.setPass(pass);
        return result;
    }
}
